/*******************************************************************************
* 
* A simple lzma86 warpper class
*
*   This class compress or decompress using the default lzma options
*   {0x5d, 0, 0, 0, 1}
* 
******************************************************************************/
#include "lzma-helper.h"
#include "file-helper.h"
#include "logging.h"
#include "LzmaLib.h"

#include <fstream>
#include <iostream>

using namespace std;
using namespace noria::diagnostic;

namespace noria{ namespace io
{
    // refer header file for detailed explaination
    bool LzmaHelper::compress(const wstring& source, const wstring& dest, size_t* olen, bool append)
    {
        ifstream is;
        ofstream os;

        is.open(source, ios::binary);
        if(is.bad())
        {
            LOG_ERROR(L"Failure opening source file, s=%s", source);
            return false;
        }

        if(append)
            os.open(dest, ios::binary | ios::app);
        else
            os.open(dest, ios::binary);
        if(os.bad())
        {
            LOG_ERROR(L"Failure opening dest file, s=%s", dest);
            return false;
        }

        auto ret = compress(is, os, olen);

        os.close();
        is.close();

        return ret;
    }

    // refer header file for detailed explaination
    bool LzmaHelper::compress(istream& source, ostream& dest, size_t* olen)
    {
        // get length of the input file:
        auto ilen = FileHelper::getFileLength(source);

        // set olen address
        size_t olen_;
        if(!olen) // if olen is not passed
        {
            olen_ = ilen; // same sized buffer as input data
            olen = &olen_;
        }
        else if(!*olen) // if olen is passed with value 0
        {
            olen_ = ilen; // same sized buffer as input data
            *olen = olen_;
        }
        else
        {
            olen_ = *olen;
        }

        // allocate memory:
        byte_ptr ibuff = new byte[ilen];
        byte_ptr obuff = new byte[olen_]; // create output buffer

	    // read data as a block:
	    source.read ((char*)ibuff, ilen);

        auto ret = compress(ibuff, ilen, obuff, olen);

        // write data to dest ostream
        if(ret)
        {
            dest.write((char*)obuff, *olen);
        }

	    delete[] ibuff;
	    delete[] obuff;

        return ret;
    }

    // refer header file for detailed explaination
    bool LzmaHelper::compress(cbyte_ptr source, size_t ilen, byte_ptr dest, size_t* olen)
    {
        byte option[5];
        size_t oplength = 5;

        int hr = LzmaCompress(dest, olen, source, ilen, option, &oplength,
            -1,
            0,
            -1,
            -1,
            -1,
            -1,
            -1);

        if(hr != SZ_OK)
        {
            LOG_ERROR("Failure compressing data, error=%d", hr);
            return false;
        }

        // debug -- output options
        auto* p = option;
        LOG_DEBUG("lzma option=%x,%x,%x,%x,%x", p[0], p[1], p[2], p[3], p[4]);

        return true;
    }

    // refer header file for detailed explaination
    bool LzmaHelper::decompress(const wstring& source, const wstring& dest, size_t* olen, bool append)
    {
        ifstream is;
        ofstream os;

        is.open(source, ios::binary);
        if(is.bad())
        {
            LOG_ERROR(L"Failure opening source file, s=%s", source);
            return false;
        }

        if(append)
            os.open(dest, ios::binary | ios::app);
        else
            os.open(dest, ios::binary);
        if(os.bad())
        {
            LOG_ERROR(L"Failure opening dest file, s=%s", dest);
            return false;
        }

        auto ret = decompress(is, os, olen);

        os.close();
        is.close();

        return ret;
    }

    // refer header file for detailed explaination
    bool LzmaHelper::decompress(istream& source, ostream& dest, size_t* olen)
    {
        // get length of the input file:
        auto ilen = FileHelper::getFileLength(source);

        // set olen address
        size_t olen_;
        if(!olen) // if olen is not passed
        {
            olen_ = ilen * 3; // triple times of input buffer
            olen = &olen_;
        }
        else if(!*olen) // if olen is passed with value 0
        {
            olen_ = ilen * 3; // triple times of input buffer
            *olen = olen_;
        }
        else
        {
            olen_ = *olen;
        }

        // allocate memory:
        byte_ptr ibuff = new byte[ilen];
        byte_ptr obuff = new byte[olen_]; // create output buffer

	    // read data as a block:
	    source.read ((char*)ibuff, ilen);

        auto ret = decompress(ibuff, ilen, obuff, olen);

        // write data to dest ostream
        if(ret)
        {
            dest.write((char*)obuff, *olen);
        }

	    delete[] ibuff;
	    delete[] obuff;

        return ret;
    }

    // refer header file for detailed explaination
    bool LzmaHelper::decompress(cbyte_ptr source, size_t ilen, byte_ptr dest, size_t* olen)
    {
        byte option[5] = {0x5d, 0, 0, 0, 1};

        int hr = LzmaUncompress(dest, olen, source, &ilen, option, 5);

        if(hr != SZ_OK && hr != SZ_ERROR_INPUT_EOF) // lzma bug -- return INPUT_EOF means OK!!
        {
            LOG_ERROR("Failure decompressing data, error=%d", hr);
            return false;
        }

        return true;
    }
}}